﻿#region header

//  ***************************************************************************
// *                                LICENSE
//  ***************************************************************************
//  * "Haplous" .NET Simple 2D Game Framework
//  * 
//  * Copyright © Jonathan Evans 2014-2014
//  * 
//  * This program is free software; you can redistribute it and/or
//  * modify it under the terms of the GNU General Public License
//  * as published by the Free Software Foundation; either version 2
//  * of the License, or (at your option) any later version.
//  * 
//  * This program is distributed in the hope that it will be useful,
//  * but WITHOUT ANY WARRANTY; without even the implied warranty of
//  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  * GNU General Public License for more details.
//  * 
//  * You should have received a copy of the GNU General Public License
//  * along with this program; if not, write to the Free Software
//  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//  * 
//  ***************************************************************************
//  * 
//  * Contact: opencollar@googlemail.com
//  * 
//  * Website: http://haplous.codeplex.com/
//  *
//  * NuGet:   http://www.nuget.org/packages/haplous/
//  *
//  * File:    Haplous/Haplous/SimplePictureSequence.cs
//  *
//  ***************************************************************************

#endregion

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

using Haplous.Core;
using Haplous.Correctness;

using JetBrains.Annotations;

// ReSharper disable once CheckNamespace

namespace Haplous
{
    /// <summary>
    ///     The algorithms that may be used by the <see cref="SimplePictureSequence" /> to select the next picture.
    /// </summary>
    public enum IterationKind
    {
        /// <summary>
        ///     Loops through the pictures from left to right (that is from the first added to the last added) jumping back to the
        ///     first when the last is reached.
        /// </summary>
        LoopLeftToRight = 0,

        /// <summary>
        ///     Loops through the pictures from right to left (that is from the last added to the first added) jumping back to the
        ///     last when the first is reached.
        /// </summary>
        LoopRightToLeft,

        /// <summary>
        ///     Loops through the pictures from left to right (that is from the first added to the last added) then backwards (from
        ///     right to left) when the last is reached.
        /// </summary>
        Scan,

        /// <summary>
        ///     Uses pictures at random.
        /// </summary>
        Random
    }

    /// <summary>
    ///     A simple sequence of pictures. This class cannot be inherited.
    /// </summary>
    [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    [DebuggerDisplay("SimplePictureSequence: Name={Name}")]
    public sealed class SimplePictureSequence : PictureSequence
    {
        /// <summary>
        ///     The algorithm to use when deciding on the next picture in the sequence.
        /// </summary>
        private readonly IterationKind _iteration;

        /// <summary>
        ///     The random number generator used to create random patterns.
        /// </summary>
        [NotNull]
        private readonly Random _random = new Random();

        /// <summary>
        ///     The direction in which a scanning sequence is moving (the increment to add to the next iteration).
        /// </summary>
        private int _direction = 1;

        /// <summary>
        ///     The index of the current picture.
        /// </summary>
        private int _index;

        /// <summary>
        ///     Initializes a new instance of the <see cref="ElementBase" /> class.
        /// </summary>
        /// <param name="game">
        ///     The game to which the class belongs.  The <paramref name="game" /> must not be
        ///     <see langword="null" />.
        /// </param>
        /// <param name="name">
        ///     The unique name that can be used to identify this object.  The <paramref name="name" /> must not be
        ///     <see langword="null" />, empty or all whitespace.
        /// </param>
        /// <param name="cue">
        ///     The Cue that will be used to trigger the iteration onto the next picture in the
        ///     sequence.  The Cue may not be <see langword="null" /> and must come from the
        ///     <see cref="Game.Cues" /> dictionary of the <paramref name="game" /> object supplied.
        /// </param>
        /// <param name="iteration">The algorithm to use when deciding on the next picture in the sequence.</param>
        /// <exception cref="Haplous.Core.UnknownResourceException">
        ///     The value provided in the <paramref name="cue" />
        ///     property cannot be found in the supplied <paramref name="game">game's</paramref><see cref="Game.Cues" />
        ///     dictionary.
        /// </exception>
        public SimplePictureSequence([NotNull] Game game, [NotNull] string name, [NotNull] Cue cue, IterationKind iteration) : base(game, name, cue)
        {
            iteration.ValidateEnumArgument("iteration");

            _iteration = iteration;

            switch(_iteration)
            {
                case IterationKind.LoopLeftToRight:
                    _index = -1;
                    _direction = +1;
                    break;

                case IterationKind.LoopRightToLeft:
                    _index = -1;
                    _direction = -1;
                    break;

                case IterationKind.Scan:
                    _index = -1;
                    _direction = +1;
                    break;

                case IterationKind.Random:
                    _index = -1;
                    _direction = 0;
                    _random = new Random();
                    break;
            }
        }

        /// <summary>
        ///     Gets the next picture in the sequence.
        /// </summary>
        /// <returns>
        ///     The picture that should now be displayed.  Must belong to the <see cref="PictureSequence.Pictures" />
        ///     dictionary and must never be <see langword="null" />.
        /// </returns>
        protected override Picture GetNext()
        {
            var count = Pictures.Count;

            switch(_iteration)
            {
                case IterationKind.LoopLeftToRight:
                    if(_index == -1)
                        _index = 0;
                    else
                        ++_index;
                    if(_index >= count)
                        _index = 0;
                    break;

                case IterationKind.LoopRightToLeft:
                    if(_index == -1)
                        _index = count - 1;
                    else
                        --_index;
                    if(_index < 0)
                        _index = count - 1;
                    break;

                case IterationKind.Scan:
                    if(_index == -1)
                        _index = 0;
                    else
                        _index += _direction;
                    if(_direction > 0)
                    {
                        if(_index > count)
                        {
                            _index = count - 1;
                            _direction = -1;
                        }
                    }
                    else
                    {
                        if(_index < 0)
                        {
                            _index = 0;
                            _direction = +1;
                        }
                    }
                    break;

                case IterationKind.Random:
                    _index = _random.Next(0, count - 1);
                    break;

                default:
                    throw new HaplousException("Impossible value found in '_iteration'.");
            }

            return Pictures[Pictures.OrderedKeys[_index]];
        }
    }
}